package net

import (
	"log"
	"strings"
	"sync"
)

type HandlerFunc func(req *WsMsgReq, rsp *WsMsgRsp)

// 中间件函数 传入参数是处理器函数(路由) 传出参数也是处理器函数 相当于对处理器函数做了一些前置操作后又返回处理器函数
type MiddlewareFunc func(handlerFunc HandlerFunc) HandlerFunc

//account login||logout
type group struct {
	mutex         sync.RWMutex
	prefix        string
	handlerMap    map[string]HandlerFunc
	middlewareMap map[string][]MiddlewareFunc // 一个路由中可能会有很多的中间件，所以这里是一个中间件列表,针对某个路由的中间件
	middlewares   []MiddlewareFunc            // 中间件列表,所有组里的中间件,组里所有的路由执行前都需要执行的中间件
}

func (r *Router) Group(prefix string) *group {
	g := &group{
		prefix:        prefix,
		handlerMap:    make(map[string]HandlerFunc),
		middlewareMap: make(map[string][]MiddlewareFunc),
	}
	r.group = append(r.group, g)
	return g
}

func (g *group) AddRouter(name string, handlerFunc HandlerFunc, middlewares ...MiddlewareFunc) { //...代表可变参列表，middlewares为[]MiddlewareFunc类型
	g.mutex.Lock()
	defer g.mutex.Unlock()
	g.handlerMap[name] = handlerFunc
	g.middlewareMap[name] = middlewares
}

func (g *group) Use(middlewares ...MiddlewareFunc) {
	g.middlewares = append(g.middlewares, middlewares...)
}

func (g *group) exec(name string, req *WsMsgReq, rsp *WsMsgRsp) {
	h, ok := g.handlerMap[name]
	if !ok {
		h, ok = g.handlerMap["*"]
		if !ok {
			log.Println("路由未定义")
		}
	}
	if ok {
		// 中间件 执行路由之前 需要执行中间件代码
		// 先处理组里的所有中间件
		for i := 0; i < len(g.middlewares); i++ {
			h = g.middlewares[i](h)
		}
		// 再处理路由的中间件
		mm, ok := g.middlewareMap[name]
		if ok {
			for i := 0; i < len(mm); i++ {
				h = mm[i](h)
			}
		}
		h(req, rsp)
	}
}

//路由代表一个一个的路径
type Router struct {
	group []*group
}

func NewRouter() *Router {
	return &Router{}
}

func (r *Router) Run(req *WsMsgReq, rsp *WsMsgRsp) {
	// req.Body.Name 路径 登录业务 account.login (account组标识) login 路由标识
	strs := strings.Split(req.Body.Name, ".")
	prefix := ""
	name := ""
	if len(strs) == 2 {
		prefix = strs[0]
		name = strs[1]
	}
	for _, g := range r.group {
		if g.prefix == prefix {
			g.exec(name, req, rsp)
		} else if g.prefix == "*" { //网关作为代理服务端需要
			g.exec(name, req, rsp)
		}
	}
}
